﻿using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Bymed.SpineScan.Service.Shared;

namespace Bymed.SpineScan.Service
{
    public class TestMessageReceiver : IDisposable
    {
        public event EventHandler MessageReceived;
        public event EventHandler DeviceConnectivityChanged;
        public string Message { get; private set; }
        private Thread _connectingDeviceThread;
        private SerialPort _serialPort;
        private bool _continue;

        public TestMessageReceiver()
        {
            InitializeSerialPort();
        }

        public bool IsDeviceConnected
        {
            get
            {
                return _serialPort.IsOpen;
            }
        }

        /// <summary>
        /// Start receiving message from serial port. This method cannot be used with CheckConnectivity at the same time.
        /// </summary>
        public void Start()
        {
            _continue = true;
            _connectingDeviceThread = new Thread(OpenSerialPortAndReadMessage);
            _connectingDeviceThread.Start();
        }

        /// <summary>
        /// Stop receiving message from serial port.
        /// </summary>
        public void Stop()
        {
            _continue = false;

            if (_connectingDeviceThread != null)
            {
                _connectingDeviceThread.Join();
            }

            if (IsDeviceConnected)
            {
                _serialPort.Close();
            }
        }

        public void Dispose()
        {
            _serialPort.Dispose();
        }

        /// <summary>
        /// Initialize the SerialPort.
        /// </summary>
        private void InitializeSerialPort()
        {
            _serialPort = new SerialPort();

            // Allow user to set the appropriate properties in App.config file.
            _serialPort.PortName = GetPortName();
            _serialPort.BaudRate = GetPortBaudRate();
            _serialPort.Parity = GetPortParity();
            _serialPort.DataBits = GetPortDataBits();
            _serialPort.StopBits = GetPortStopBits();
            _serialPort.Handshake = GetPortHandshake();
            _serialPort.ReadTimeout = GetReadTimeout();
            _serialPort.WriteTimeout = GetWriteTimeout();
        }

        /// <summary>
        /// Open Serial Port and Read Message
        /// </summary>
        private void OpenSerialPortAndReadMessage()
        {
            OpenSerialPort();
            ReadMessage();
        }

        /// <summary>
        /// Open the serial port.
        /// </summary>
        private void OpenSerialPort()
        {
            if (!IsDeviceConnected)
            {
                try
                {
                    _serialPort.Open();
                }
                catch { }

                RaiseDeviceConnectivityChangedEvent();
            }
        }

        /// <summary>
        /// Read Message from device.
        /// </summary>
        private void ReadMessage()
        {
            while (_continue)
            {
                if (IsDeviceConnected)
                {
                    try
                    {
                        Message = _serialPort.ReadLine();
                        RaiseMessageReceivedEvent();
                    }
                    catch (TimeoutException) { }
                    catch
                    {
                        // Device might be unplugged 
                        RaiseDeviceConnectivityChangedEvent();
                    }
                }
                else
                {
                    Thread.Sleep(GetReadTimeout());
                    OpenSerialPort();
                }
            }
        }

        /// <summary>
        /// Raise the DeviceConnectivityChanged event.
        /// </summary>
        private void RaiseDeviceConnectivityChangedEvent()
        {
            if (DeviceConnectivityChanged != null)
            {
                DeviceConnectivityChanged(this, new EventArgs());
            }
        }

        /// <summary>
        /// Raise the MessageReceived event.
        /// </summary>
        private void RaiseMessageReceivedEvent()
        {
            if (MessageReceived != null)
            {
                MessageReceived(this, new EventArgs());
            }
        }

        private string GetPortName()
        {
            return (string)ConfigFileUtility.GetValue(ConfigurationConstant.SerialPortName);
        }

        private int GetPortBaudRate()
        {
            return Convert.ToInt32(ConfigFileUtility.GetValue(ConfigurationConstant.SerialPortBaudRate));
        }

        private Parity GetPortParity()
        {
            return (Parity)Enum.Parse(typeof(Parity), ConfigFileUtility.GetValue(ConfigurationConstant.SerialPortParity).ToString());
        }

        private int GetPortDataBits()
        {
            return Convert.ToInt32(ConfigFileUtility.GetValue(ConfigurationConstant.SerialPortDataBitse));
        }

        private StopBits GetPortStopBits()
        {
            return (StopBits)Enum.Parse(typeof(StopBits), ConfigFileUtility.GetValue(ConfigurationConstant.SerialPortStopBits).ToString());
        }

        private Handshake GetPortHandshake()
        {
            return (Handshake)Enum.Parse(typeof(Handshake), ConfigFileUtility.GetValue(ConfigurationConstant.SerialPortHandshake).ToString());
        }

        private int GetReadTimeout()
        {
            return Convert.ToInt32(ConfigFileUtility.GetValue(ConfigurationConstant.SerialPortReadTimeout));
        }

        private int GetWriteTimeout()
        {
            return Convert.ToInt32(ConfigFileUtility.GetValue(ConfigurationConstant.SerialPortWriteTimeout));
        }
    }
}